﻿/*              Copyright(c) 2008 Dave Sexton              *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using DaveSexton.Controls;
using DaveSexton.DocToMaml.Maml;
using DaveSexton.DocToMaml.Extensions;

namespace DaveSexton.DocToMaml
{
	internal sealed class HtmlToMamlConverter
	{
		#region Public Properties
		public static readonly Encoding DefaultEncoding = Encoding.UTF8;

		public int ProcessedImageCount { get; private set; }

		public int ProcessedLinkCount { get; private set; }
		#endregion

		#region Private / Protected
		private readonly FileGuidManager guids;
		private readonly ConceptualImageManager images;
		private readonly IRuleContainer rules;
		private readonly RuleCollection removableTagNames, replaceableTagNames, removableAttributes, replaceableAttributes;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="HtmlToMamlConverter" /> class.
		/// </summary>
		public HtmlToMamlConverter(IRuleContainer rules, ConceptualImageManager imageManager, FileGuidManager guidManager)
		{
			if (rules == null)
				throw new ArgumentNullException("rules");

			if (imageManager == null)
				throw new ArgumentNullException("imageManager");

			if (guidManager == null)
				throw new ArgumentNullException("guidManager");

			this.rules = rules;
			this.images = imageManager;
			this.guids = guidManager;

			removableTagNames = rules.GetRules(RuleAction.Remove, RuleMatchKind.TagName);
			removableAttributes = rules.GetRules(RuleAction.Remove, RuleMatchKind.Attribute);
			replaceableTagNames = rules.GetRules(RuleAction.Replace, RuleMatchKind.TagName);
			replaceableAttributes = rules.GetRules(RuleAction.Replace, RuleMatchKind.Attribute);
		}
		#endregion

		#region Methods
		public string ConvertFileToMaml(string file, Guid guid)
		{
			if (string.IsNullOrEmpty(file))
				throw new ArgumentException(Resources.Errors.NullOrEmpty, "file");

			return ConvertToMaml(System.IO.File.ReadAllText(file), guid);
		}

		public string ConvertToMaml(string html, Guid guid)
		{
			if (string.IsNullOrEmpty(html))
				return string.Empty;

			return SecondPassConvert(FirstPassConvert(html), guid);
		}

		private string FirstPassConvert(string html)
		{
			try
			{
				StringBuilder output = new StringBuilder();

				using (HtmlToXmlConverter converter = new HtmlToXmlConverter(output))
				{
					converter.TagNameProcessor = ProcessTagName;
					converter.AttributeNameProcessor = ProcessAttributeName;
					converter.RemoveTagPrefixes = true;

					converter.Convert(html);
				}

				return output.ToString();
			}
			catch (Exception ex)
			{
				throw new HtmlToMamlConversionException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
					Resources.Errors.HtmlToXmlConversionExceptionMessage, Environment.NewLine, ex.Message), ex);
			}
		}

		private string SecondPassConvert(string xml, Guid guid)
		{
			try
			{
				DeveloperConceptualDocument document = new DeveloperConceptualDocument(rules, images, guids);

				XmlWriterSettings settings = new XmlWriterSettings();
				settings.Indent = true;
				settings.Encoding = DefaultEncoding;

				using (System.IO.MemoryStream memory = new System.IO.MemoryStream(xml.Length + 1024))
				{
					using (XmlWriter writer = XmlWriter.Create(memory, settings))
					{
						using (System.IO.StringReader input = new System.IO.StringReader(xml))
						{
							XmlReaderSettings readerSettings = new XmlReaderSettings();
							readerSettings.IgnoreComments = true;
							readerSettings.IgnoreProcessingInstructions = true;
							readerSettings.IgnoreWhitespace = true;

							using (XmlReader reader = XmlReader.Create(input, readerSettings))
							{
								document.ConvertFromHtml(guid, reader, writer);
							}
						}
					}

					ProcessedImageCount = document.ProcessedImageCount;
					ProcessedLinkCount = document.ProcessedLinkCount;

					return DefaultEncoding.GetString(memory.GetBuffer());
				}
			}
			catch (Exception ex)
			{
				throw new HtmlToMamlConversionException(string.Format(System.Globalization.CultureInfo.CurrentCulture, 
					Resources.Errors.XmlToMamlConversionExceptionMessage, Environment.NewLine, ex.Message), ex);
			}
		}

		private string ProcessTagName(string name)
		{
			if (removableTagNames.Contains(name))
				return null;
			else if (replaceableTagNames.Contains(name))
				return (string) replaceableTagNames[name].Value;
			else
				return name;
		}

		private string ProcessAttributeName(string name)
		{
			if (removableAttributes.Contains(name))
				return null;
			else if (replaceableAttributes.Contains(name))
				return (string) replaceableAttributes[name].Value;
			else
				return name;
		}
		#endregion
	}
}
